package org.tulkas.robot.http;

import java.io.IOException;
import java.io.OutputStream;

import org.apache.http.client.methods.HttpRequestBase;
import org.tulkas.robot.http.headers.RequestHeaders;
import org.tulkas.robot.http.headers.ResponseHeaders;

/**
 * Base class of a Http call. Including http request and response information. e.g. url, status code, headers, etc.
 *
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 */
public abstract class BaseConn {
	public static final int STATUS_NOT_STARTED = 0;
	public static final int STATUS_QUEUED = 1;
	public static final int STATUS_DOWNLOADING = 2;
	public static final int STATUS_FINISHED = 3;

	/** 
	 * the URL of the web page. 
	 */
	private String url;

	/** 
	 * the response status code. 
	 */
	private int httpStatusCode;

	/** 
	 * the response reason phase string. 
	 */
	private String httpReasonPhase;

	/**
	 * whether the http request been redirected.
	 */
	private boolean redirected;

	/**
	 * whether the http request is get from cache.
	 */
	private boolean cached;

	/**
	 * whether the http request get interrupted when downloading.
	 */
	private boolean interrupted = false;

	/**
	 * exception occurred during the http connection.
	 */
	private Exception exception;

	/** 
	 * the request headers. 
	 */
	private final RequestHeaders requestHeaders = new RequestHeaders();

	/** 
	 * the response headers. 
	 */
	private final ResponseHeaders responseHeaders = new ResponseHeaders();

	/**
	 * the length of the response data been recieved
	 */
	private long receivedLength = 0;

	/**
	 * the status of this http connection
	 */
	private int status = STATUS_NOT_STARTED;

	/**
	 * Gets the URL of the web page.
	 *
	 * @return the URL of the web page
	 */
	public synchronized String getUrl() {
		return url;
	}

	/**
	 * Sets the URL of the web page.
	 *
	 * @param url the new URL of the web page
	 */
	public synchronized void setUrl(String url) {
		this.url = url;
	}

	/**
	 * Gets the response status code.
	 *
	 * @return the response status code
	 */
	public synchronized int getHttpStatusCode() {
		return httpStatusCode;
	}

	/**
	 * Sets the response status code.
	 *
	 * @param statusCode the new response status code
	 */
	public synchronized void setHttpStatusCode(int statusCode) {
		this.httpStatusCode = statusCode;
	}

	/**
	 * Gets the response reason phase string.
	 *
	 * @return the response reason phase string
	 */
	public synchronized String getHttpReasonPhase() {
		return httpReasonPhase;
	}

	/**
	 * Sets the response reason phase string.
	 *
	 * @param reasonPhase the new response reason phase string
	 */
	public synchronized void setHttpReasonPhase(String reasonPhase) {
		this.httpReasonPhase = reasonPhase;
	}

	/**
	 * Gets the request headers.
	 *
	 * @return the request headers
	 */
	public synchronized RequestHeaders getRequestHeaders() {
		return requestHeaders;
	}

	/**
	 * Gets the response headers.
	 *
	 * @return the response headers
	 */
	public synchronized ResponseHeaders getResponseHeaders() {
		return responseHeaders;
	}

	/**
	 * Checks if there is error in the http call. The Error could be a HTTP error(40x or 50x response), 
	 * or an exception during runtime.
	 *
	 * @return true, if there is error or not
	 */
	public synchronized boolean isError() {
		if (exception != null)
			return true;
		else if (httpStatusCode >= 400 && httpStatusCode < 600)
			return true;
		else
			return false;
	}

	/**
	 * Checks if is whether the http request been redirected.
	 *
	 * @return the whether the http request been redirected
	 */
	public synchronized boolean isRedirected() {
		return redirected;
	}

	/**
	 * Sets the whether the http request been redirected.
	 *
	 * @param redirected the new whether the http request been redirected
	 */
	public synchronized void setRedirected(boolean redirected) {
		this.redirected = redirected;
	}

	/**
	 * Checks if is whether the http request is get from cache.
	 *
	 * @return the whether the http request is get from cache
	 */
	public synchronized boolean isCached() {
		return cached;
	}

	/**
	 * Sets the whether the http request is get from cache.
	 *
	 * @param cached the new whether the http request is get from cache
	 */
	public synchronized void setCached(boolean cached) {
		this.cached = cached;
	}

	/**
	 * Gets the length of the response data been recieved.
	 *
	 * @return the length of the response data been recieved
	 */
	public synchronized long getReceivedLength() {
		return receivedLength;
	}

	/**
	 * Sets the length of the response data been recieved.
	 *
	 * @param receivedLength the new length of the response data been recieved
	 */
	public synchronized void setReceivedLength(long receivedLength) {
		this.receivedLength = receivedLength;
	}

	/**
	 * Checks if is whether the http request get interrupted when downloading.
	 *
	 * @return the whether the http request get interrupted when downloading
	 */
	public synchronized boolean isInterrupted() {
		return interrupted;
	}

	/**
	 * Sets the whether the http request get interrupted when downloading.
	 *
	 * @param interrupted the new whether the http request get interrupted when downloading
	 */
	public synchronized void setInterrupted(boolean interrupted) {
		this.interrupted = interrupted;
	}

	/**
	 * reset the Http connection. Usually used when reusing the connection for reconnect.
	 */
	public synchronized void resetResponse() {
		cached = false;
		interrupted = false;
		httpReasonPhase = null;
		receivedLength = 0;
		redirected = false;
		httpStatusCode = 0;
		exception = null;
		responseHeaders.clear();
		status = STATUS_NOT_STARTED;
	}

	public synchronized Exception getException() {
		return exception;
	}

	public synchronized void setException(Exception exception) {
		this.exception = exception;
	}

	public synchronized int getStatus() {
		return status;
	}

	public synchronized void setStatus(int status) {
		this.status = status;
	}

	/**
	 * create a proper OutputStream for downloading, based on the type of this http request.
	 * 
	 * @return
	 * @throws IOException
	 */
	public abstract OutputStream createResponseStream() throws IOException;

	/**
	 * generate the HttpRequestBase object based on the type of this http request.
	 * 
	 * @param options
	 * @return
	 * @throws IOException
	 */
	public abstract HttpRequestBase createHttpRequest(ClientOptions options);

	/**
	 * handle the response of the http request.
	 * 
	 * @param rawResponse
	 * @param options
	 * @throws IOException
	 */
	public abstract void handleResponseContent(OutputStream resStream,
			ClientOptions options) throws IOException;

	@Override
	public synchronized int hashCode() {
		return (url == null) ? 0 : url.hashCode();
	}

}
